﻿using System;
using System.Collections.Generic;
using System.Data;

namespace Nasa8x.Core
{
    public static class DataComparerExtensions
    {
        /// <summary>
        /// Sorts the rows according to the specified sort expression.
        /// </summary>
        /// <typeparam name="TRow">Type of DataRow in sequence (must inherit System.Data.DataRow)</typeparam>
        /// <param name="source">EnumerableRowCollection to sort.</param>
        /// <param name="sortExpression">SQL-like sort expression specifying the column names to order according to, e.g. Column1, Column2 DESC.</param>
        /// <returns>An System.Data.OrderedEnumerableRowCollection&lt;TRow&gt; ordered according to the given sequence.</returns>
        /// <exception cref="System.ArgumentNullException"><paramref name="source"/> is null or <paramref name="sortExpression"/> is null</exception>
        /// <exception cref="Dynamite.Parsing.ParserException">If <paramref name="sortExpression"/> is an invalid sort expression.</exception>
        public static OrderedEnumerableRowCollection<TRow> OrderBy<TRow>(this EnumerableRowCollection<TRow> source, string sortExpression) where TRow : DataRow
        {
            if (source == null) throw new ArgumentNullException("source");
            if (sortExpression == null) throw new ArgumentNullException("sortExpression");
            DataColumnCollection columns;
            using (IEnumerator<TRow> enumerator = source.GetEnumerator())
            {
                if (enumerator.MoveNext() == false) return source.OrderBy(r => r);
                columns = enumerator.Current.Table.Columns;
            }
            OrderedEnumerableRowCollection<TRow> result = null;

            SimpleTokenizer tokenizer = new SimpleTokenizer(sortExpression);
            do
            {
                String colName = tokenizer.ReadIdentity();
                if (colName.Length == 0)
                {
                    throw new ParserException(tokenizer.Position, tokenizer.Expression, "Column name expected.");
                }
                int colIndex = columns.IndexOf(colName);
                if (colIndex < 0) throw new ParserException(tokenizer.Position, tokenizer.Expression, "Column '" + colName + "' not found in table.");
                DataColumn column = columns[colIndex];

                bool ascending = true;
                if (tokenizer.AdvanceIfIdent("DESC") || tokenizer.AdvanceIfIdent("DESCENDING"))
                {
                    ascending = false;
                }
                else if (tokenizer.AdvanceIfIdent("ASC") || tokenizer.AdvanceIfIdent("ASCENDING"))
                {
                    ascending = true;
                }

                if (result == null)
                {
                    if (column.DataType == typeof(String))
                    {
                        result = ascending ? source.OrderBy(r => r.Field<String>(colIndex), StringComparer.CurrentCultureIgnoreCase) :
                                             source.OrderByDescending(r => r.Field<String>(colIndex), StringComparer.CurrentCultureIgnoreCase);
                    }
                    else
                    {
                        result = ascending ? source.OrderBy(r => r.Field<Object>(colIndex)) :
                                             source.OrderByDescending(r => r.Field<Object>(colIndex));
                    }
                }
                else
                {
                    if (column.DataType == typeof(String))
                    {
                        result = ascending ? result.ThenBy(r => r.Field<String>(colIndex), StringComparer.CurrentCultureIgnoreCase) :
                                             result.ThenByDescending(r => r.Field<String>(colIndex), StringComparer.CurrentCultureIgnoreCase);
                    }
                    else
                    {
                        result = ascending ? result.ThenBy(r => r.Field<Object>(colIndex)) :
                                             result.ThenByDescending(r => r.Field<Object>(colIndex));
                    }
                }
            }
            while (tokenizer.AdvanceIfSymbol(','));
            tokenizer.ExpectEnd();
            return result;
        }
    }
}
